package api

import (
	"bytes"
	"gitee.com/gopher2011/godemo/config"
	"gitee.com/gopher2011/godemo/global"
	"gitee.com/gopher2011/godemo/logger"
	"gitee.com/gopher2011/godemo/server/dao"
	"gitee.com/gopher2011/godemo/server/service"
	"gitee.com/gopher2011/godemo/util"
	echo "github.com/labstack/echo/v4"
	"github.com/polaris1119/times"
	"html/template"
	"math"
	"math/rand"
	"path/filepath"
	"strings"
	"time"
)

// 自定义模版函数
var funcMap = template.FuncMap{
	// 获取 avatar头像
	"gravatar":util.Gravatar,

	//转为前端显示需要的时间格式
	"formatTime": func(i interface{})string{
		ctime,ok := i.(string)
		if !ok {
			return ""
		}
		t,_ := time.Parse("2006-01-02 15:04:05", ctime)
		return t.Format(time.RFC3339 + "+08:00")
	},

	"format": func(i interface{},format string)string {
		switch i.(type) {
		case time.Time:
			return (i.(time.Time)).Format(format)
		case int64:
			value := i.(int64)
			return time.Unix(value,0).Format(format)
		}
		return ""
	},

	"hasPrefix": func(s,prefix string)bool {
		if strings.HasPrefix(s,prefix) {
			return true
		}
		return false
	},

	"substring":util.Substring,
	"add": func(num ...interface{}) int{//累加
		total := 0
		for _,item := range num {
			if n,ok := item.(int);ok {
				total += n
			}
		}
		return total
	},
	"sub": func(num1,num2 int)int {// 减
		return num1 - num2
	},
	"mod":func(num1,num2 int)int { //取模运算
		if num1 == 0 {
			num1 = rand.Intn(500)
		}
		return num1 % num2
	},
	"divide": func(num1,num2 int) int{ //除法
		return int(math.Ceil(float64(num1))/float64(num2))
	},
	"explode": func(s,sep string)[]string {
		return strings.Split(s,sep)
	},
	"noescape": func(s string) template.HTML{//不使用js中的escape()
		return template.HTML(s)
	},
	"timestamp":func(ts ...time.Time) int64{
		if len(ts) >0 {
			return ts[0].Unix()
		}
		return time.Now().Unix()
	},
	"distanceDay": func(i interface{}) int{
		var (
			t time.Time
			err error
		)
		switch val := i.(type){
		case string:
			t,err = time.ParseInLocation("2006-01-02 15:04:05", val, time.Local)
			if err != nil {
				return 0
			}
		case time.Time:
			t = val
		case dao.LocalTime:
			t = time.Time(val)
		}
		return int(time.Now().Sub(t).Hours()/24)
	},
	"canEdit":,
	"canPublish":,
	"parse"
}

const (
	LayoutTpl = "common/layout.html" //模版文件layout.html的路径,该文件具体的路径是:template/common/layout.html
	AdminLayoutTpl = "common.html"
)

// 创建一个新的HTML模版
func tplInclude(file string,data map[string]interface{})template.HTML{
	var buffer = &bytes.Buffer{}
	//返回一个模版对象(或者说是模版句柄)
	tpl,err := template.New(filepath.Base(file)).Funcs(funcMap).ParseFiles(config.TemplateDir + file)
	if err != nil {
		logger.Errorf("parse template file(%s) error :%v\n",file,err)
		return ""
	}
	// 将已解析的模板应用于指定的数据对象，并将输出写入buffer中
	err = tpl.Execute(buffer,data)
	if err != nil {
		logger.Errorf("template file(%s) syntax error:%v\n",file,err)//syntax:语法
		return ""
	}
	// 将数据渲染到 HTML上
	return template.HTML(buffer.String())
}

// 是否访问这些页面
var filterPathes = map[string]struct{}{
	"/account/login":     {},
	"/account/register":  {},
	"/account/forgetpwd": {},
	"/account/resetpwd":  {},
	"/topics/new":        {},
	"/topics/modify":     {},
	"/resources/new":     {},
	"/resources/modify":  {},
	"/articles/new":      {},
	"/articles/modify":   {},
	"/project/new":       {},
	"/project/modify":    {},
	"/book/new":          {},
	"/wiki/new":          {},
	"/wiki/modify":       {},
}

// Render html 输出
// 参数<contentTpl>是要被渲染的模版文件,例如: user/list.html,user/query.html
// 参数<data>是要被渲染的具体数据
func Render(ctx echo.Context,contentTpl string,data map[string]interface{})error{
	if data == nil {
		data = map[string]interface{}{}
	}

	//objLog := service.GetLogger() //该函数在service/common.go中

	contentTpl = LayoutTpl + "," + contentTpl //每个模版文件必须带上 layout.html这个布局文件
	htmlFiles := strings.Split(contentTpl,",")

	for i,html := range htmlFiles {
		htmlFiles[i] = config.TemplateDir + html
	}

	// 为了使用自定义的模板函数，首先New一个以第一个模板文件名为模板名。
	// 这样，在ParseFiles时，新返回的*Template便还是原来的模板实例
	// 使用自定义的模版函数,并解析模版文件
	tpl,err := template.New("layout.html").Funcs(funcMap).Funcs(template.FuncMap{"include":tplInclude}).ParseFiles(htmlFiles...)
	if err != nil {
		logger.Errorf("解析模版出错:[%q]%s\n",ctx.Request().RequestURI,err)
		return err
	}

	if strings.Contains(ctx.Request().UserAgent(),"miniProgram"){//用户代理是微信小程序(User Agent:它是一个特殊字符串头，使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件)
		data["min_program"] = true
	}else {
		data["pos_ad"] = service.Advertisement.FindAll(ctx.Path())
	}

	data["cur_time"] = times.Format("Y-m-d H:i:s")
	data["path"] = ctx.Path()
	data["filter"] = false

	if _,ok := filterPathes[ctx.Path()];ok {//如果访问的是 filterPathes中的页面
		data["filter"] = true
	}

	hasLoginMission := false
	me,ok := ctx.Get("user").(*dao.Me)
	if ok {
		// 每日登陆奖励(做完登陆任务则有奖励)
		hasLoginMission = service.Mission.HasLoginMission(me)
	}

	data ["has_login_mission"] = hasLoginMission

}

// 解析模版
func executeTpl(ctx echo.Context,tpl *template.Template,data map[string]interface{})error{

	// 如果没有定义css 和 js模板,则定义之
	// 输入模板名称,返回该名称对应的具体模板
	if jsTpl := tpl.Lookup("js");jsTpl == nil {
		tpl.Parse(`{{define "js"}}{{end}}`)
	}
	if cssTpl := tpl.Lookup("css");cssTpl == nil {
		tpl.Parse(`{{define "css"}}{{end}}`)
	}
	// 如果没有seo模板则定义
	if seoTpl := tpl.Lookup("seo");seoTpl == nil {
		tpl.Parse(`{{define "seo"}}
		<meta name="keywords" content="` + dao.WebsiteSet.SeoKeyword + `">
		<meta name="description" content="` + dao.WebsiteSet.SeoDescription + `">
		{{end}}`)
	}

	// 当前用户信息
	curUser,ok := ctx.Get("user").(*dao.Me)
	if ok {
		data["me"] = curUser
	}else {
		data["me"] = &dao.Me{}
	}

	// websocket 主机
	if global.OnlineEnv(){
		data["wshost"] = global.App.Domain
	}else {
		data["wshost"] = global.App.Host + ":" + global.App.Port
	}

	global.App.SetUptime()
	global.App.SetCopyright()

}